/*
 * note:for register manage.
 */

package com.guaniu.voip.sip;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;


import android.os.Handler;
import android.text.format.Time;
import android.util.Log;
 
  
public class NetworkManager implements INetworkManager{
	private final String TAG = "NetworkManager";
	static private NetworkManager gNetworkManagerInstance; 
	private LinkedList<NetworkCallback> mNetworkCallbackList;  
	private LinkedList<NetWorkInfo> mNetWorkInfo;  
	private Handler mHandler; 
	private int mDelayTime = 0;
	private NETWORK_STATUS mLastNetworkStatus = NETWORK_STATUS.NETWORK_STATUS_INIT;
	private NETWORK_STATUS mNetworkStatus = NETWORK_STATUS.NETWORK_STATUS_CONNECTED;
	private NETWORK_DELAY_STATUS mDelayStatus;
	private NetWorkInfo mOldNetWorkInfo = null;
	private NetWorkInfo mCurrNetWorkInfo = null;
	private int highDelayCount = 0;//������綶��
	
	public static synchronized NetworkManager getInstance(){
		if (gNetworkManagerInstance == null){
			gNetworkManagerInstance = new NetworkManager();
		}
		return gNetworkManagerInstance;
	}
	
	public static synchronized void releaseInstance(){
		if (gNetworkManagerInstance != null){
			gNetworkManagerInstance.release();
		}
		gNetworkManagerInstance = null;
	}

	public void release(){ 
		if (mHandler != null){
			mHandler.getLooper().quit();
		}
	}

	private NetworkManager(){  
		UserHandlerThread thread = new UserHandlerThread();
		thread.initHandler();
		mHandler = thread.getHandler();
		mNetWorkInfo = new LinkedList<NetWorkInfo>();
		mNetworkCallbackList = new LinkedList<NetworkCallback>(); 
	} 
	
	public boolean isSameNetwork(){
		if (mOldNetWorkInfo == null){
			return false;
		}
		
		if (mCurrNetWorkInfo.isSameNetWork(mCurrNetWorkInfo)){
			return true;
		}
		
		return false;
	}
	
	public void registerCallback(NetworkCallback delegate){
		synchronized (mNetworkCallbackList) {
			mNetworkCallbackList.add(delegate);
		}
		
		postToUserCallback();
	}

	public void startMonitorNetworkAsync(){
	}
	
	public void startTimerToMonitorNetwork(int delayMs){
		Log.d(TAG, "startTimerToMonitorNetwork, mDelayTime:"+mDelayTime);
		mDelayTime = delayMs;
//		mHandler.postDelayed(timerMonitorNetwork, delayMs); 
	}
	
	public void stopTimerToMonitorNetwork(){
		Log.d(TAG, "stopTimerToMonitorNetwork");
		mDelayTime = 0;
	}

    
	public void unRegisterCallback(NetworkCallback delegate){
		synchronized (mNetworkCallbackList) {
			for(int i=0; i<mNetworkCallbackList.size(); i++){
				if (mNetworkCallbackList.get(i) == delegate){
					mNetworkCallbackList.remove(i);
					break;
				}
			}
		}
	}
	
	Runnable timerMonitorNetwork =new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
//			CallManager.getInstance().checkSessionRcvFrameTimeout();
//			
//			if (NativeManagerService.getInstance() != null){
//				NativeManagerService.getInstance().startMonitorNetworkSync();	
//			}

			if (mDelayTime > 0){
				mHandler.postDelayed(timerMonitorNetwork, mDelayTime);
			}
		}			
	};
	
	Runnable notifyUserCallback = new Runnable(){

		@Override
		public void run() {
			if (mNetworkStatus == NETWORK_STATUS.NETWORK_STATUS_INIT){
				return;
			}
			// TODO Auto-generated method stub
			synchronized (mNetworkCallbackList) {
				for (NetworkCallback cl : mNetworkCallbackList) {
					cl.networkStatus(mNetworkStatus);
				}  
			} 
		}};
		
	Runnable notifyUserDelayCallback = new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			for (NetworkCallback cl : mNetworkCallbackList) {
				cl.networkDelayStatus(mDelayStatus);;
			}  
	}};

	/*
	 * 1.save the network info nearly 3 times .
	 * 2.if the all delay > 300 notify user.
	 */
	Runnable checkNetworkRunnable = new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			int low = 0; 
			int high = 0;
			int middle = 0;
			int high_high = 0; 
			int count = 0;
			synchronized (mNetWorkInfo) {
				count = mNetWorkInfo.size();
				for (NetWorkInfo info : mNetWorkInfo) {
					if (info.mDelayRate < 100){
						low++;
					}else if (info.mDelayRate < 300){
						middle++;
					}else if (info.mDelayRate < 500){
						high++;
					}else{
						high_high++;
					}
				}
			} 
			if (low == count){
				mDelayStatus = NETWORK_DELAY_STATUS.NETWORK_DELAY_NORMAL;
			}
			if (middle == count){
				mDelayStatus = NETWORK_DELAY_STATUS.NETWORK_DELAY_MIDDLE;
			}
			if (high == count){
				mDelayStatus = NETWORK_DELAY_STATUS.NETWORK_DELAY_HIGH;
			}
			if (high_high == count){
				highDelayCount++;
				if(highDelayCount >= 2){					
					mDelayStatus = NETWORK_DELAY_STATUS.NETWORK_DELAY_VERY_HIGH;
					highDelayCount = 0;
				}
			}
			Log.d(TAG, "checkNetworkRunnable low:"+low+", middle:"+middle+", high:"+high+", high_high:"+high_high + ", count:"+count);
			Log.d(TAG, "mDelayStatus:"+mDelayStatus);
			
			mHandler.removeCallbacks(notifyUserDelayCallback);
			mHandler.post(notifyUserDelayCallback);
			
			if (low + middle + high == count){
				if (mDelayStatus == NETWORK_DELAY_STATUS.NETWORK_DELAY_VERY_HIGH){
					notifyListerner(NETWORK_STATUS.NETWORK_STATUS_CONNECTED_BUT_NET_DELAY_HIGH);
				}
			}
		}
	};
	
	private final int CHECK_NEARLY_TIMES = 2;
	public void saveMonitorNetWorkInfo(NetWorkInfo networkinfo){
		mOldNetWorkInfo = mCurrNetWorkInfo;
		mCurrNetWorkInfo = networkinfo;
		
		synchronized(mNetWorkInfo){
			if (mNetWorkInfo.size() >= CHECK_NEARLY_TIMES){
				mNetWorkInfo.removeFirst();
			}
			mNetWorkInfo.add(networkinfo);

			Log.d(TAG, "mNetWorkInfo size:"+mNetWorkInfo.size());
			if (mNetWorkInfo.size() == CHECK_NEARLY_TIMES){
				mHandler.removeCallbacks(checkNetworkRunnable);
				mHandler.post(checkNetworkRunnable);
			}
		}
	}
	
	public void notifyListerner(final NETWORK_STATUS status){ 
		Log.e(TAG, "notifyListerner status:"+status);  
//		if (mDelayTime == 0){
			setStatus(status);
//		}
		postToUserCallback();
	}
	
	private void postToUserCallback(){
		if (mNetworkCallbackList.size() > 0) {
			mHandler.removeCallbacks(notifyUserCallback);
			mHandler.post(notifyUserCallback);
		}
	}
	
	private void setStatus(NETWORK_STATUS status){
		mLastNetworkStatus = mNetworkStatus;
		mNetworkStatus = status;
	}

	public NETWORK_STATUS getLastNetworkStatus(){   
		return mLastNetworkStatus; 
	}
	
	public NETWORK_STATUS getNetworkStatus(){   
		return mNetworkStatus; 
	}
	
	public NETWORK_DELAY_STATUS getDelayStatus(){
		return mDelayStatus;
	}
	
	public boolean isDisconnected(){
		if (!isNetworkConnected()){
			return true;
		}
		if (mOldNetWorkInfo == null){
			return true;
		}
		if (!mOldNetWorkInfo.isEqual(mCurrNetWorkInfo)){
			return true;
		}
		return false;
	}
	
	public boolean isNetworkConnected(){   
		return (mNetworkStatus == NETWORK_STATUS.NETWORK_STATUS_CONNECTED)
				||(mNetworkStatus == NETWORK_STATUS.NETWORK_STATUS_CONNECTED_2G)
				||(mNetworkStatus == NETWORK_STATUS.NETWORK_STATUS_CONNECTED_3G)
				||(mNetworkStatus == NETWORK_STATUS.NETWORK_STATUS_CONNECTED_4G)
				||(mNetworkStatus == NETWORK_STATUS.NETWORK_STATUS_CONNECTED_BUT_NET_DELAY_HIGH)
				; 
	}
	public static final int MAX_DELAY_TIME = 400;
	enum NetType{
		NETTYPE_INIT,
		NETTYPE_MOBILE,
		NETTYPE_WIFI
	}
	public static class NetWorkInfo{
		public NetType mNetType;
		public String mNetName;
		public boolean mIsConnected = true;
		public int mPacketLossRate;//10/100
		public float mMaxDelayRate;
		public float mDelayRate;
		public long mTime;

		public void clone(NetWorkInfo copy){
			mNetType = copy.mNetType;
			mNetName = copy.mNetName;
			mIsConnected = copy.mIsConnected;
			mPacketLossRate = copy.mPacketLossRate;
			mDelayRate = copy.mDelayRate;
			mTime = copy.mTime;
		}

		public boolean isInitialized(){
			if (mNetName != null && mNetName.length() > 0){
				return true;
			}

			return false;
		}

		public boolean isEqual(NetWorkInfo network){
			if (network.mNetType == mNetType &&
					network.mNetName.equals(mNetName) &&
					network.mIsConnected == mIsConnected){
				return true;
			}
			return false;
		}

		public boolean isSameNetWork(NetWorkInfo network){
			if (network.mNetType == mNetType &&
					network.mNetName.equals(mNetName)){
				return true;
			}
			return false;
		}

		public String toString(){
			StringBuilder sb = new StringBuilder();
			sb.append("time:");
			Time t = new Time();
			t.set(mTime);
			sb.append(String.format("%d-%d-%d:%d:%d,", t.month+1, t.monthDay, t.hour, t.minute, t.second));
			sb.append("type:");
			sb.append(mNetType);
			sb.append(",name:");
			sb.append(mNetName);
			sb.append(",connect:");
			sb.append(mIsConnected);
			sb.append(",mPacketLossRate:");
			sb.append(mPacketLossRate);
			sb.append(",avg mDelayRate:");
			sb.append(mDelayRate);
			sb.append(",max mMaxDelayRate:");
			sb.append(mMaxDelayRate);
			return sb.toString();
		}
	}

}
